package org.springframework.aop.framework;



import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.aop.framework.autoproxy.A17_2;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * @author wtcode
 * @Description
 * @date 2025/4/9 - 11:55
 */
public class A18 {

    static class Aspect{

        @Before("execution(* foo())")
        public void before1(){
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2(){
            System.out.println("before2");
        }

        public void after(){
            System.out.println("after");
        }

        @AfterReturning("execution(* foo())")
        public void afterReturning(){
            System.out.println("afterReturning");
        }

        @AfterThrowing("execution(* foo())")
        public void afterThrowing(Exception e){
            System.out.println("afterThrowing" + e.getMessage());
        }

        @Around("execution(* foo())")
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            return null;
        }

    }

    static class Target{
        public void foo(){
            System.out.println("target foo");
        }
    }

    public static void main(String[] args) throws Throwable {

        SingletonAspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
        List<Advisor> advisors = new ArrayList<>();
        for (Method method : Aspect.class.getDeclaredMethods()) {
            if (method.isAnnotationPresent(Before.class)) {
                // 切点
                String expression = method.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                // 通知类
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method,pointcut,factory);
                Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
                advisors.add(advisor);
            }else if(method.isAnnotationPresent(AfterReturning.class)){
                String expression = method.getAnnotation(AfterReturning.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method,pointcut,factory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
                advisors.add(advisor);
            }else if(method.isAnnotationPresent(AfterThrowing.class)){

                String epression = method.getAnnotation(AfterThrowing.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(epression);

                AspectJAfterThrowingAdvice advice = new AspectJAfterThrowingAdvice(method,pointcut,factory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
                advisors.add(advisor);
            }else if(method.isAnnotationPresent(Around.class)){

                String epression = method.getAnnotation(Around.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(epression);

                AspectJAroundAdvice advice = new AspectJAroundAdvice(method,pointcut,factory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,advice);
                advisors.add(advisor);
            }
        }


        for (Advisor advisor : advisors) {
            System.out.println(advisor);
        }


        /*
         * @Before 前置通知会被转换为下面原始的AspectMethodBeforeAdvice形式，该对象包含了如下信息
         *   a.通知类代码从哪里来
         *   b.切点是什么
         *   c.通知对象如何创建，本例共同用一个Aspect对象
         *   类似的通知还有
         *    1.AspectJAroundAdvice(环绕通知)
         *    2.AspectJAfterReturningAdvice
         *    3.AspectJAfterThrowingAdvice
         *    4.AspectJAfterAdvice
         */


        // 2.通知统一转换为环绕通知 MethodInterceptor

        Target target = new Target();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(Target.class);
        proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
        proxyFactory.addAdvisors(advisors);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
        List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
        for (Object o : methodInterceptorList) {
            System.out.println(o);
        }


        System.out.println("--------------------------------------------->>>>>>>>>");
        /*
         * 统一转换为methodInterceptor，环绕通知，这体现在方法名中的Interceptor
         * 适配如下：
         *      MethodBeforeAdviceAdaptor将@Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
         *      AfterReturningAdviceAdaptor将AspectJAfterReturningAdvice 适配为 AfterReturningInterceptor
         *
         */

        // 3.创建并执行调用链
        MethodInvocation methodInvocation = new ReflectiveMethodInvocation(null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList);
        methodInvocation.proceed();





    }
}
